package com.huiquan.elasticsearch.service;

import com.huiquan.analysis.dao.AnalysisSentenceDao;
import com.huiquan.analysis.domain.AnalysisSearchParam;
import com.huiquan.analysis.domain.AnalysisSentence;
import com.huiquan.analysis.domain.AnalysisSentenceVocabularyES;
import com.huiquan.analysis.service.SimilarityAlgo;
import com.huiquan.analysis.utils.ListUtil;
import com.huiquan.framework.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;

/**
 * Created by chenqiang
 * Date: 2018/5/29
 * Time: 上午11:38
 * Elasticsearch搜索引擎服务提供service
 */
@Slf4j
@Service
public class ElasticsearchService {
    @Autowired
    private TransportClient client;

    @Autowired
    private AnalysisSentenceDao analysisSentenceDao;

    /**
     * 从es中取得未标注主诉列表
     *
     * @param searchParams
     * @return
     */
    public List<AnalysisSentence> getZSUnlabeledListFromES(List<AnalysisSearchParam> searchParams) {
        int labelStatus = 0;
        return getZSListFromES(searchParams, labelStatus);
    }

    /**
     * 从es中取得已标注主诉列表
     *
     * @param searchParams
     * @return
     */
    public List<AnalysisSentence> getZSLabeledListFromES(List<AnalysisSearchParam> searchParams) {
        int labelStatus = 1;
        return getZSListFromES(searchParams, labelStatus);
    }

    /**
     * 从es中取得主诉列表
     *
     * @param searchParams
     * @param labelStatus
     * @return
     */
    private List<AnalysisSentence> getZSListFromES(List<AnalysisSearchParam> searchParams, int labelStatus) {
        List<AnalysisSentence> result = new ArrayList<>();


        LinkedHashMap<Long, String[]> resultMap = null;
        List<Map<String, Object>> esResult;
        AnalysisSentence sentence;
        boolean needDealSomeFlag = false;

        if (searchParams == null || searchParams.size() == 0) {
            //在空检索的情况下，需要去重处理
            needDealSomeFlag = true;
            searchParams = new ArrayList<>();
            AnalysisSearchParam searchParam = new AnalysisSearchParam();
            searchParam.setVocabulary("");
            searchParam.setProperty("");
            searchParam.setPosition1("");
            searchParam.setPosition2("");
            searchParam.setOppositePropertyFlag("0");
            searchParam.setOppositePosition1Flag("0");
            searchParams.add(searchParam);
        }
        // 查询
        for (int i = 0; i < searchParams.size(); i++) {
            esResult = getZSListFromESBySingle(searchParams.get(i), labelStatus);
            if (esResult == null || esResult.size() == 0) {
                return result;
            }
            if (i == 0) {
                resultMap = getResultMapES(esResult);
            } else {
                // 得到多个查询结果中的交集部分
                resultMap = ListUtil.getRepeatResult(resultMap, getResultMapES(esResult));
            }
        }
        if (resultMap == null || resultMap.size() == 0) {
            return result;
        }
        for (Map.Entry<Long, String[]> entry : resultMap.entrySet()) {
            sentence = new AnalysisSentence();
            sentence.setId(entry.getKey());
            sentence.setSentence(entry.getValue()[0]);
            sentence.setGmtModified(DateUtils.getDateStrFromUnixTimestamp(entry.getValue()[1]));
            result.add(sentence);
            if (result.size() >= 5000 && !needDealSomeFlag) {
                return result;
            }
        }

        // 如果list不为空，做去重处理
        if (ListUtil.isNotEmpty(result) && needDealSomeFlag) {
            List<HashMap<String, Object>> mapList = analysisSentenceDao
                    .selectVocabularySidPropertyMapper(getIdListStr(result));
            result = removeSameItem(result, mapList);
        }


        return result;
    }

    /**
     * 手动增加ES索引
     *
     * @param list
     */
    public void addZSContentToES(List<AnalysisSentenceVocabularyES> list) {
        if (list != null && list.size() > 0) {
            for (AnalysisSentenceVocabularyES entity : list) {
                try {
                    XContentBuilder content = XContentFactory.jsonBuilder()
                            .startObject()
                            .field("id", entity.getId())
                            .field("sid", entity.getSid())
                            .field("sentence", entity.getSentence())
                            .field("vocabulary", entity.getVocabulary())
                            .field("property", entity.getProperty())
                            .field("position1", entity.getPosition1())
                            .field("position2", entity.getPosition2())
                            .field("label_status", entity.getLabelStatus())
                            .field("gmt_modified", entity.getGmtModified())
                            .endObject();
                    IndexResponse result = this.client.prepareIndex("zsvocabandsentence", "zsvocabandsentence")
                            .setSource(content)
                            .get();
                    log.info("添加所以成功，id为;{}", result.getId());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * 根据句子id删除,删除对应所有的数据索引
     *
     * @param sid
     */
    public void deleteZSContentFromESBySid(long sid) {
        BoolQueryBuilder booleanQuery = QueryBuilders.boolQuery();
        booleanQuery.must(QueryBuilders.matchQuery("sid", sid));
        SearchRequestBuilder builder = this.client.prepareSearch("zsvocabandsentence")
                .setTypes("zsvocabandsentence")
                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                .setQuery(booleanQuery)
                .setFrom(0)
                .setSize(10000);

        log.info(builder.toString());

        SearchResponse response = builder.get();

        for (SearchHit hit : response.getHits()) {
            System.out.println("所要删除的索引_id:" + hit.getId());
            DeleteResponse dResponse = this.client.prepareDelete("zsvocabandsentence", "zsvocabandsentence", hit.getId()).execute()
                    .actionGet();
            if (dResponse.isFound()) {
                log.info("索引{}删除成功", hit.getId());
            } else {
                log.info("索引{}删除失败", hit.getId());
            }
        }
        log.info("总数量:{}", response.getHits().getTotalHits());
        log.info("耗时:{}", response.getTookInMillis());
    }

    /**
     * 使用单个搜索条件从es中检索主诉列表
     *
     * @param searchParam
     * @param labelStatus
     * @return
     */
    private List<Map<String, Object>> getZSListFromESBySingle(AnalysisSearchParam searchParam, int labelStatus) {
        BoolQueryBuilder booleanQuery = QueryBuilders.boolQuery();
        SortBuilder sortBuilder = SortBuilders.fieldSort("gmt_modified")
                .order(SortOrder.DESC);
        booleanQuery.must(QueryBuilders.matchQuery("label_status", labelStatus));
        if (StringUtils.isNotEmpty(searchParam.getVocabulary())) {
            booleanQuery.must(QueryBuilders.matchQuery("vocabulary", searchParam.getVocabulary()));
        }
        if (StringUtils.isNotEmpty(searchParam.getProperty())) {
            if ("0".endsWith(searchParam.getOppositePropertyFlag())) {
                booleanQuery.must(QueryBuilders.matchQuery("property", searchParam.getProperty()));
            } else {
                booleanQuery.mustNot(QueryBuilders.matchQuery("property", searchParam.getProperty()));
            }
        }
        if (StringUtils.isNotEmpty(searchParam.getPosition1())) {
            if ("0".endsWith(searchParam.getOppositePosition1Flag())) {
                booleanQuery.must(QueryBuilders.matchQuery("position1", searchParam.getPosition1()));
            } else {
                booleanQuery.mustNot(QueryBuilders.matchQuery("position1", searchParam.getPosition1()));
            }
        }
        if (StringUtils.isNotEmpty(searchParam.getPosition2())) {
            booleanQuery.must(QueryBuilders.matchQuery("position2", searchParam.getPosition2()));
        }

        SearchRequestBuilder builder = this.client.prepareSearch("zsvocabandsentence")
                .setTypes("zsvocabandsentence")
                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                .setQuery(booleanQuery)
                .addSort(sortBuilder)
                .setFrom(0)
                .setSize(10000);

        System.out.println(builder);

        SearchResponse response = builder.get();
        List<Map<String, Object>> result = new ArrayList<>();

        for (SearchHit hit : response.getHits()) {
            result.add(hit.getSource());
        }
        System.out.println("总数量：" + response.getHits().getTotalHits());
        System.out.println("耗时：" + response.getTookInMillis());
        return result;
    }

    /**
     * 从语句list中得到id以,拼接的字符串
     *
     * @param list
     * @return
     */
    private String getIdListStr(List<AnalysisSentence> list) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < list.size(); i++) {
            sb.append("'").append(list.get(i).getId()).append("'");
            if (i < list.size() - 1) {
                sb.append(",");
            }
        }
        return sb.toString();
    }

    /**
     * 移除列表中相似度较高的项目
     *
     * @param list
     * @param mapList
     * @return
     */
    private List<AnalysisSentence> removeSameItem(List<AnalysisSentence> list, List<HashMap<String, Object>> mapList) {
        if (list == null || list.size() <= 1) {
            return list;
        }
        HashMap<Long, String> propertyMap = new HashMap<>();
        for (HashMap<String, Object> map : mapList) {
            propertyMap.put((long) map.get("sid"), String.valueOf(map.get("property")));
        }
        List<AnalysisSentence> result = new ArrayList<>();
        result.add(list.get(0));
        for (int i = 1; i < list.size(); i++) {
            boolean flag = true;
            for (int j = 1; j <= 10; j++) {
                if ((i - j) < 0) {
                    break;
                }
                if (StringUtils.isEmpty(propertyMap.get(list.get(i).getId()))) {
                    log.info("ID为:{}的语句找不到对应的属性集合", list.get(i).getId());
                    continue;
                }
                if (StringUtils.isEmpty(propertyMap.get(list.get(i - j).getId()))) {
                    log.info("ID为:{}的语句找不到对应的属性集合", list.get(i - j).getId());
                    continue;
                }
                double d = SimilarityAlgo.getSimilarity(propertyMap.get(list.get(i).getId()),
                        propertyMap.get(list.get(i - j).getId()));
                if (d > 0.93D) {
                    flag = false;
                }
            }
            if (flag) {
                result.add(list.get(i));
            }
        }
        return result;
    }

    /**
     * 得到去重后的结果集合
     *
     * @return
     */
    private LinkedHashMap<Long, String[]> getResultMapES(List<Map<String, Object>> list) {
        LinkedHashMap<Long, String[]> map = new LinkedHashMap<>();
        String sid;
        String sentence;
        String gmtModified;
        for (Map<String, Object> m : list) {
            sid = String.valueOf(m.get("sid"));
            sentence = String.valueOf(m.get("sentence"));
            gmtModified = String.valueOf(m.get("gmt_modified"));
            map.put(Long.valueOf(sid), new String[]{sentence, gmtModified});
        }
        return map;
    }

}
